/** @file
@brief General utility functions and types. */
#ifndef PRYN_UTILITY_H
#define PRYN_UTILITY_H

#include <pryn/platform.h>

typedef struct PrynFileDescription PrynFileDescription; ///< A file node when iterating over files.
typedef struct PrynTime PrynTime; ///< A moment in time.
typedef struct PrynRectangle PrynRectangle; ///< A two-dimensional rectangle.

#include <pryn/result.h>
#include <pryn/state.h>
#include <pryn/string.h>

/** @defgroup PrynRectangle PrynRectangle C API
@{ */

	PrynImport (PrynResult, prynRectangleNull, (PrynRectangle *rectangle)) ///< A rectangular consisting only of zeroes.
	PrynImport (PrynResult, prynRectangle, (PrynRectangle *rectangle, int left, int top, int right, int bottom)) ///< Create a new rectangle, simply copying the values. This can lead to an invalid rectangle with left above right or top above bottom. Use #prynRectangleContaining to sort such a rectangle.
	PrynImportVariadic (PrynResult, prynRectangleContaining, (PrynRectangle *rectangle, size_t pointCount, ...)) ///< Create a new rectangle that includes all points, where each is an int (x, y) pair.
	PrynImport (PrynResult, prynRectangleContainingPointer, (PrynRectangle *rectangle, size_t pointCount, int *points)) ///< Create a new rectangle that includes all points, where each is a (x, y) pair.
	PrynImport (PrynResult, prynRectangleExpand, (PrynRectangle *rectangle, const PrynRectangle *source, int amountX, int amountY)) ///< Expand a rectangle on all sides by the given amount.
	
	/// A two-dimensional integer area.
	struct PrynRectangle
	{
		int left; ///< Left edge, inclusive.
		int top; ///< Top edge, inclusive.
		int right; ///< Right edge, exclusive.
		int bottom; ///< Bottom edge, exclusive.

	#if __cplusplus
		inline PrynRectangle () : left (0), top (0), right (0), bottom (0) {} ///< Constructor that clears to all zeroes.
		inline PrynRectangle (int left, int top, int right, int bottom) : left (left), top (top), right (right), bottom (bottom) {} ///< Assign all the parameters. This can lead to an invalid rectangle with left above right or top above bottom. Use #Containing to sort such a rectangle.
		static inline PrynRectangle Containing (size_t pointCount, ...) { va_list arguments; va_start (arguments, pointCount); PrynRectangle result; prynCheckErrorNull (prynRectangleContainingPointer (&result, pointCount, (int *) arguments)); va_end (arguments); return result; } ///< Create a new rectangle that includes all points, where each is an int (x, y) pair.
		static inline PrynRectangle ContainingPointer (size_t pointCount, int *points) { PrynRectangle result; prynCheckErrorNull (prynRectangleContainingPointer (&result, pointCount, points)); return result; } ///< Create a new rectangle that includes all points, where each is an (x, y) pair.

		inline PrynRectangle expand (int amountX, int amountY) { PrynRectangle result; prynCheckErrorNull (prynRectangleExpand (&result, this, amountX, amountY)); return result; } ///< Expand the rectangle on all sides by the given amount.
	#endif /* __cplusplus */
	};

/** @} */

/** @defgroup PrynTime PrynTime C API
@{ */

	PrynImport (PrynResult, prynTimeCreate, (PrynTime *time, int year, int month, int day, int hour, int minute, int second)) ///< Create a time from the parametes.
	PrynImport (PrynResult, prynTimePrint, (PrynTime *time, PrynState *state)) ///< Print the time in the form "YYYY/MM/DD HH:MM:SS".
	PrynImport (PrynResult, prynTimeEqual, (const PrynTime *a, const PrynTime *b)) ///< Returns #PrynResultTrue if the times are equal, #PrynResultFalse if not. #PrynIsTrue can be used to test for truth.

	/// A moment in time.
	struct PrynTime
	{
		int year; ///< Year, where positive is CE and negative is BCE.
		unsigned int remainder; ///< Collection of seconds, minutes, hours, days, and months.

	#ifdef __cplusplus
		inline PrynTime (int year, int month, int day, int hour, int minute, int second); ///< Create a time from the parameters.

		inline void print (PrynState *state); ///< Print the time in the form "YYYY/MM/DD HH:MM:SS".

		inline int second (); ///< Retrieve the zero-based seconds of this time.
		inline int minute (); ///< Retrieve the zero-based minutes of this time.
		inline int hour (); ///< Retrieve the zero-based hour of this time.
		inline int day (); ///< Retrieve the zero-based day of this time.
		inline int month (); ///< Retrieve the zero-based month of this time.

		inline bool operator == (const PrynTime &other) { PrynResult result = prynTimeEqual (this, &other); prynCheckErrorNull (result); return PrynIsTrue (result); } ///< Return whether the times are equal.
		inline bool operator != (const PrynTime &other) { return !(*this == other); } ///< Return whether the times are unequal.
	#endif /* __cplusplus */
	};

	/** @name Shifts and Masks
		These define the shifts, bits, and masks for taking apart the @e remainder field in a #PrynTime. They're used to build the macros, which are preferable.
		@{ */

	#define PrynTimeSecondShift 0 /**< @brief Shift to isolate the seconds in the remainder. */
	#define PrynTimeSecondBits 6 /**< @brief Number of bits in the seconds in the remainder */
	#define PrynTimeSecondMask ((1 << PrynTimeSecondBits) - 1) /**< @brief Mask to isolate the seconds in the remainder after shifting. */ 
	#define PrynTimeMinuteShift (PrynTimeSecondShift + PrynTimeSecondBits) /**< @brief Shift to isolate the minutes in the remainder. */
	#define PrynTimeMinuteBits 6 /**< @brief Number of bits in the minutes in the remainder. */
	#define PrynTimeMinuteMask ((1 << PrynTimeMinuteBits) - 1) /**< @brief Mask to isolate the minutes in the remainder after shifting. */
	#define PrynTimeHourShift (PrynTimeMinuteShift + PrynTimeMinuteBits) /**< @brief Shift to isolate the hours in the remainder. */
	#define PrynTimeHourBits 5 /**< @brief Number of bits in the house in the remainder. */
	#define PrynTimeHourMask ((1 << PrynTimeHourBits) - 1) /**< @brief Mask to isolate the house in the remainder aftershifting. */
	#define PrynTimeDayShift (PrynTimeHourShift + PrynTimeHourBits) /**< @brief Shift to isolate the day in the remainder. */
	#define PrynTimeDayBits 5 /**< @brief Number of bits in the day in the remainder. */
	#define PrynTimeDayMask ((1 << PrynTimeDayBits) - 1) /**< @brief Mask to isolate the day in the remainder after shifting. */
	#define PrynTimeMonthShift (PrynTimeDayShift + PrynTimeDayBits) /**< @brief Shift to isolate the month in the remainder. */
	#define PrynTimeMonthBits 4 /**< @brief Number of bits in the month in the remainder. */
	#define PrynTimeMonthMask ((1 << PrynTimeMonthBits) - 1) /**< @brief Mask to isolate the month in the remainder after shifting. */
	#define prynTimeRemainderValue(TIME, NAME) (((TIME)->remainder >> PrynTime##NAME##Shift) & PrynTime##NAME##Mask) /**< Used to isolate fields. */

	/** @} */

	#define prynTimeSecond(TIME) prynTimeRemainderValue (TIME, Second) /**< Retrieve the seconds value in the #PrynTime, zero-based. */
	#define prynTimeMinute(TIME) prynTimeRemainderValue (TIME, Minute) /**< Retrieve the minutes value in the #PrynTime, zero-based. */
	#define prynTimeHour(TIME) prynTimeRemainderValue (TIME, Hour) /**< Retrieve the hour value in the #PrynTime, zero-based. */
	#define prynTimeDay(TIME) prynTimeRemainderValue (TIME, Day) /**< Retrieve the day value in the #PrynTime, zero-based. */
	#define prynTimeMonth(TIME) prynTimeRemainderValue (TIME, Month) /**< Retrieve the month value in the #PrynTime, zero-based. */
	#define prynTimeYear(TIME) ((TIME)->year) /**< Retrieve the year value in the #PrynTime, where positive is CE and negative is BCE. */

	#if __cplusplus
	inline PrynTime::PrynTime (int year, int month, int day, int hour, int minute, int second) { prynCheckErrorNull (prynTimeCreate (this, year, month, day, hour, minute, second)); }

	inline void PrynTime::print (PrynState *state) { PrynResult result = prynTimePrint (this, state); prynCheckErrorBase (state, result); }

	int PrynTime::second () { return prynTimeSecond (this); }
	int PrynTime::minute () { return prynTimeMinute (this); }
	int PrynTime::hour () { return prynTimeHour (this); }
	int PrynTime::day () { return prynTimeDay (this); }
	int PrynTime::month () { return prynTimeMonth (this); }
	#endif /* __cplusplus */

/** @} */

/** @defgroup PrynFileDescription PrynFileDescription C API
@{ */

	PrynImport (PrynResult, prynFileBeginIteration, (PrynState *state, PrynFileDescription *file, const PrynString *path))
		/**< @brief Begin iterating over the files in the path.

		This is perhaps the best use of this function set:

@code
PrynState *state = ...; // Initialised to a valid value.
PrynFileDescription file; // Receives file descriptions.
PrynString path; // The path to search.
PrynResult result; // Result codes from the functions.

result = prynFileBeginIteration (state, &file, path); // Start iteration.
while (!result) // 0 is PrynResultSuccess.
{
	// Process the file.
	result = prynFileNextIteration (&file); // Move to the next file.
}
prynFileEndIteration (&file); // Destroy any remaining data.
// Handle erroneous result code if desired.
@endcode

		@param state The state to use for allocations.
		@param[out] file Receives the file data on success; whatever data already there will be overwritten.
		@param path The path to iterate over files. If it's a relative path, it's relative to the current working directory.
		@returns #PrynResultSuccess if a file was found, #PrynResultDone if iteration has stopped, or an error code; see #PrynResult.
		*/

	PrynImport (PrynResult, prynFileNextIteration, (PrynFileDescription *self)) ///< Read the next file in the iteration, returning #PrynResultSuccess if there was a file, or #PrynResultDone if the iteration is complete.
	PrynImport (PrynResult, prynFileEndIteration, (PrynFileDescription *self)) ///< Terminate file iteration, destroying any resources.
	
	/// A file node when iterating over files.
	struct PrynFileDescription
	{
		PrynState *state; ///< State information.
		PrynString path; ///< Path to the file; for example, if the filename is "test/foo.bar", this will be "test/".
		PrynString filename; ///< Base filename; for example, if the file is "test/foo.bar", this will be "foo.bar".
		PrynString extension; ///< Extension of the filename; for example, if the file is "test/foo.bar", this will be ".bar". This is empty if there is no extension, such as for "test.bar/baz".
		PrynString fullname; ///< Full filename including path; if the file is "test/foo.bar", this will be "test/foo.bar".
		void *platform; ///< Additional opaque state used by the iterator; do not touch.
		PrynTime modified; ///< When the file was last modified.

	#ifdef __cplusplus
		inline bool beginIteration (PrynState *state, const PrynString &path) { PrynResult result = prynFileBeginIteration (state, this, &path); return PrynIsTrue (prynCheckErrorBase (state, result)); }
			/**< @brief Begin iterating over the files in the path.

			This is perhaps the best use of this function set:

@code
PrynState *state = ...; // Initialised to a valid value.
PrynFileDescription file; // Receives file descriptions.
PrynString path; // The path to search.
bool result; // Result codes from the functions.

result = file.beginIteration (state, path); // Start iteration.
while (result) // 0 is PrynResultSuccess.
{
	// Process the file.
	result = file.nextIteration (); // Move to the next file.
}
file.endIteration (); // Destroy any remaining data.
@endcode

			@param state The state to use for allocations.
			@param path The path to iterate over files. If it's a relative path, it's relative to the current working directory.
			@returns @b true if a file was found; @b false if none were found.
			*/

		inline bool nextIteration () { PrynResult result = prynFileNextIteration (this); return PrynIsTrue (prynCheckErrorBase (state, result)); } ///< Read the next file in the iteration, returning @b true if there is a file, or @b false if the iteration is complete.
		inline void endIteration () { prynFileEndIteration (this); } ///< Terminate file iteration, destroying any resources.		
	#endif /* __cplusplus */
	};

/** @} */

/** @defgroup Mathematics Mathematics Functions
@{ */

	/** @brief Return @e MINIMUM if @e VALUE is less than that, @e MAXIMUM if @e VALUE is greater than that, or @e VALUE. */
	#define prynMiddle(MINIMUM, VALUE, MAXIMUM) ((VALUE) < (MINIMUM) ? (MINIMUM) : (VALUE) > (MAXIMUM) ? (MAXIMUM) : (VALUE))

	#define prynMaximum(A, B) ((A) > (B) ? (A) : (B)) /**< @brief Return the maximum of the two values. */
	#define prynMaximumAssign(A, B) ((A) = prynMax ((A), (B))) /**< @brief Assign @e A to the maximum of the two values. */
	#define prynMinimum(A, B) ((A) > (B) ? (B) : (A)) /**< @brief Return the minimum of the two values. */
	#define prynAbsolute(VALUE) ((VALUE) < 0 ? -(VALUE) : (VALUE)) /**< @brief Return the absolute value (positive) of @e VALUE. */
	#define prynSwap(TYPE, A, B) do { TYPE C = (A); (A) = (B); (B) = C; } while (0) /**< @brief Swap the values of @e A and @e B, with @e TYPE as the shared type between them. */
	#define prynSquare(VALUE) ((VALUE) * (VALUE)) /**< @brief Multiply @e VALUE by itself. */

/** @}
@defgroup MemoryProcessing Memory Processing Functions
@{ */

	typedef int (*PrynOrderFunction) (const void *a, const void *b, void *data); ///< Sorting function for prynSort.

	PrynImport (PrynResult, prynSort, (void *pointer, size_t each, size_t length, PrynOrderFunction order, void *data)) ///< Sort the elements in the array at pointer. @e each is the number of bytes per element, @e length is the number of elements, @e order returns negative to put a before b and positive, (including zero) to put a after b, and @e data is passed to order.
	PrynImport (PrynOrderFunction, prynOrderInteger, ()) ///< Helper for #prynSort that sorts integers.
	PrynImport (PrynOrderFunction, prynOrderReverse, ()) ///< Helper for #prynSort that takes a #PrynOrderFunction in the data parameter, then swaps the arguments so that the list will be sorted reverse.

	PrynImport (PrynResult, prynCopyMemory, (void *target, const void *source, uintptr_t length)) ///< Copy a number of bytes from the source to the target. If the two arrays overlap, the results are undefined.
	PrynImport (PrynResult, prynMemoryEqual, (const void *a, const void *b, uintptr_t length)) ///< Return whether these two blocks of memory are equal.
	PrynImport (PrynResult, prynSetMemory, (void *data, char value, uintptr_t length)) ///< Clear some memory to the given value.
	#define prynClearMemory(DATA, LENGTH) prynSetMemory ((DATA), 0, (LENGTH)) /**< @brief Clear some memory to zero. */

	#define prynClearObject(POINTER) prynSetMemory ((POINTER), 0, sizeof (*(POINTER))) /**< @brief Clear the object pointed to to zero. */

/** @} */

/** @defgroup Colors Colors Functions
@{ */

	typedef uint32_t PrynColor; ///< An RGBA color, where red is in the first 8 bits, green is in the second 8 bits (shift 8), blue is in the third 8 bits (shift 16), and alpha is in the last 8 bits (shift 24) where 255 is opaque.

	#define prynColorClamp(V) prynMiddle (0, (int) (V), 255)

	/// Create a color out of RGBA (red green blue alpha) parameters. Each is from 0 (black/transparent) to 255 (white/opaque). They are saturated into this range.
	#define prynColorRGBA(R, G, B, A) (prynColorClamp (R) | (prynColorClamp (G) << 8) | (prynColorClamp (B) << 16) | (prynColorClamp (A) << 24))

	#define prynColorRGB(R, G, B) prynColorRGBA (R, G, B, 255) /**< @brief Create a color out of RGB (red green blue) parameters, with an implicit alpha of opaque. Each is from 0 (black) to 255 (white). */
	#define prynColorR(COLOR) ((COLOR) & 255) /**< @brief Extract the red component from the color, which is from 0 (black) to 255 (red). */
	#define prynColorG(COLOR) (((COLOR) >> 8) & 255) /**< @brief Extract the green component from the color, which is from 0 (black) to 255 (green). */
	#define prynColorB(COLOR) (((COLOR) >> 16) & 255) /**< @brief Extract the blue component from the color, which is from 0 (black) to 255 (blue). */
	#define prynColorA(COLOR) (((COLOR) >> 24) & 255) /**< @brief Extract the alpha component from the color, which is from 0 (transparent) to 255 (opaque). */

	#define prynColorWhite prynColorRGB (255, 255, 255) /**< @brief The brightest white color. */
	#define prynColorBlack prynColorRGB (0, 0, 0) /**< @brief The darkest black color. */
	#define prynColorRed prynColorRGB (255, 0, 0) /**< @brief The most saturated red color. */
	#define prynColorGreen prynColorRGB (0, 255, 0) /**< @brief The most saturated green color. */
	#define prynColorBlue prynColorRGB (0, 0, 255) /**< @brief The most saturated blue color. */
	#define prynColorGray(VALUE) prynColorRGB ((VALUE), (VALUE), (VALUE)) /**< @brief A gray color of the specific intensity, from 0 (black) to 255 (white). */
	#define prynColorGrey(VALUE) prynColorGray ((VALUE)) /**< @brief A grey color of the specific intensity, from 0 (black) to 255 (white). */

/** @} */

/** @defgroup Lists Lists Functions
@{ */

	#define prynDListAppendEx(LIST, OBJECT, FIRST, LAST, NEXT, PREVIOUS) \
		do { \
			if ((LIST).LAST) (LIST).LAST->NEXT = (OBJECT); \
			else (LIST).FIRST = (OBJECT); \
			(OBJECT)->PREVIOUS = (LIST).LAST; \
			(OBJECT)->NEXT = 0; \
			(LIST).LAST = (OBJECT); \
		} while (0)

	/// Append an element to a double-linked list.
	#define prynDListAppend(LIST, OBJECT) prynDListAppendEx ((LIST), (OBJECT), first, last, next, previous)

	#define prynDListAppendP(LIST, OBJECT) prynDListAppendEx ((LIST), (OBJECT), first, last, pNext, pPrevious)
			
	/// Remove an element from a double-linked list.
	#define prynDListRemoveEx(LIST, OBJECT, FIRST, LAST, NEXT, PREVIOUS) \
		do { \
			if ((OBJECT)->NEXT) (OBJECT)->NEXT->PREVIOUS = (OBJECT)->PREVIOUS; \
			else (LIST).LAST = (OBJECT)->PREVIOUS; \
			if ((OBJECT)->PREVIOUS) (OBJECT)->PREVIOUS->NEXT = (OBJECT)->NEXT; \
			else (LIST).FIRST = (OBJECT)->NEXT; \
			(OBJECT)->NEXT = 0; \
			(OBJECT)->PREVIOUS = 0; \
		} while (0)

	#define prynDListRemove(LIST, OBJECT) prynDListRemoveEx ((LIST), (OBJECT), first, last, next, previous)
	#define prynDListRemoveP(LIST, OBJECT) prynDListRemoveEx ((LIST), (OBJECT), first, last, pNext, pPrevious)

	/// Iterate over the elements in a double-linked list.
	#define prynDListEach(NAME, LIST) for (NAME = (LIST).first; NAME; NAME = NAME->next)
	#define prynDListEachP(NAME, LIST) for (NAME = (LIST).first; NAME; NAME = NAME->pNext)

	/// Iterate over the elements in a double-linked list in reverse. TYPE should not normally be a pointer.
	#define prynDListEachReverse(NAME, LIST) for (NAME = (LIST).last; NAME; NAME = NAME->previous)

	/// Iterate over the elements in a linear list.
	#define prynForEach(NAME, START, COUNT) for (NAME = (START); NAME < (START) + (COUNT); NAME ++) 

	#define prynDListCountP(LIST, NAME, COUNTER) for (NAME = (LIST)->first, COUNTER = 0; NAME; NAME = NAME->pNext, COUNTER ++)

/** @} */

#endif /* PRYN_UTILITY_H */
